﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Threading;
using DeepZoomPivotConstructor.VisualLib.Utilities;

namespace DeepZoomPivotConstructor.VisualLib
{
    /// <summary>
    /// Generates a visuual that is a jpeg. Once the file is written out, we assume
    /// that it will always remain there -- that no one else will alter it.
    /// </summary>
    [Serializable]
    public class VisualBitmapImage : VisualBasicSingleImage
    {
        /// <summary>
        /// Where we can write our temp images.
        /// </summary>
        public DirectoryInfo ImageWritingDirectory { get; set; }

        /// <summary>
        /// Internally keep track of the template.
        /// </summary>
        //static LazyInit<DirectoryInfo> _global_image_dir = new LazyInit<DirectoryInfo>(() => new DirectoryInfo(Path.GetTempPath()));
        static DirectoryInfo _global_image_dir = new DirectoryInfo(Path.GetTempPath());

        /// <summary>
        /// Global directory -- new versions of this object will write images
        /// here.
        /// </summary>
        public static DirectoryInfo GlobalImageWritingDirectory
        {
            get { return _global_image_dir; }
            set { _global_image_dir = value; }
        }

        /// <summary>
        /// Create a bitmap image with the x and y size, and the
        /// appropriate DPI.
        /// </summary>
        /// <param name="xinches"></param>
        /// <param name="yinches"></param>
        /// <param name="dpi"></param>
        public VisualBitmapImage(double xinches, double yinches, int dpi, Color color)
        {
            _width = xinches;
            _height = yinches;

            _dots_x = (int) (_width * dpi);
            _dots_y = (int) (_height * dpi);

            if (_dots_x > 15000 || _dots_y > 15000)
            {
                throw new ArgumentException("Image size out of range");
            }

            _color = color;

            _dpi = dpi;

            ImageWritingDirectory = GlobalImageWritingDirectory;
        }

        private int _dots_x = 0;
        private int _dots_y = 0;

        private Color _color;

        private int _dpi;

        public override List<SceneGraphSceneNode> SGNodeList
        {
            get
            {
                FileInfo f = GenerateImage();
                SceneGraphSceneNode n = new SceneGraphSceneNode();
                n.FileName = f.FullName;
                n.Height = Height;
                n.Width = Width;
                n.x = XPosition;
                n.y = YPosition;
                n.ZOrder = _zIndex;
                List<SceneGraphSceneNode> result = new List<SceneGraphSceneNode>();
                result.Add(n);
                return result;
            }
        }

        /// <summary>
        /// Where we store the cached version of the jpeg image.
        /// </summary>
        private FileInfo _cachedJPEG = null;

        /// <summary>
        /// Generate the image, or return the cached versoin of the image.
        /// </summary>
        /// <returns></returns>
        private FileInfo GenerateImage()
        {
            if (_cachedJPEG != null)
            {
                return _cachedJPEG;
            }

            ///
            /// Get a temp file
            /// 

            FileInfo temp = new FileInfo(ImageWritingDirectory.FullName + "\\" + Path.GetRandomFileName() + ".jpg");


            ///
            /// Create the image (make sure to use the "using" clause so
            /// that we correctly release the memory when we are done. If this
            /// is a high resolution image it can be a fair amount of memory!!
            /// 

            using (Bitmap bit = new Bitmap(_dots_x, _dots_y))
            {
                using (Graphics g = Graphics.FromImage(bit))
                {
                    Brush b = new SolidBrush(_color);
                    g.FillRectangle(b, 0, 0, _dots_x, _dots_y);

                    _drawOps.ForEach(item => item.Draw(g));

                    bit.Save(temp.FullName);
                    _cachedJPEG = temp;
                }
            }
            return _cachedJPEG;
        }

        [Serializable]
        private abstract class drawInfo
        {
            public abstract void Draw(Graphics g);
        }

        [Serializable]
        private class drawText : drawInfo
        {
            PointF _p;
            int _h = 0;
            string _text = "";
            public drawText(int xpos, int ypos, int height, string text)
            {
                _p = new PointF(xpos, ypos);
                _h = height;
                _text = text;
            }

            /// <summary>
            /// Do the actual draw.
            /// </summary>
            /// <param name="g"></param>
            public override void Draw(Graphics g)
            {
                using (Font f = new Font("Ariel", _h, FontStyle.Regular, GraphicsUnit.Pixel))
                {
                    g.DrawString(_text, f, Brushes.White, _p);
                }
            
            }
        }

        private List<drawInfo> _drawOps = new List<drawInfo>();

        /// <summary>
        /// Put some text on the jPEG. This needs to be called before we do the render!
        /// </summary>
        /// <param name="xpos">X position (inches)</param>
        /// <param name="ypos">Y position (inches)</param>
        /// <param name="textheight">How high the text is</param>
        /// <param name="text">what text to place there</param>
        public void AddText(double xpos, double ypos, double textheight, string text)
        {
            ///
            /// If we have already written out the jpeg, bomb!
            /// 

            if (_cachedJPEG != null)
            {
                throw new VisException("JPEG has been written out already -- can't call AddText any longer");
            }

            ///
            /// Add to the list of text to draw when we do the render.
            /// 

            _drawOps.Add(new drawText((int) (xpos * _dpi), (int)(ypos * _dpi), (int)(textheight * _dpi), text)); 
        }
    }
}
